package server.analytics;

/**
 * This class represents an analytics server of an auction-system.
 * AnalyticsServerImpl implements the interface AnalyticsServer that
 * contains three methods that are being invoked remotely.
 * These methods are: subscribe, unsubscribe, processEvent.
 * The AnalyticsServerImpl 
 *
 * @version 1.0 vom 12.11.2012
 * @author Distributed Group 111 1026312, 1026309
 */

import java.rmi.NoSuchObjectException;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.HashMap;

import client.management.ManagementClientNotifier;

import model.event.*;

public class AnalyticsServerImpl implements AnalyticsServer {

	private ArrayList<Subscription> subscriptions;

	private HashMap<String, Long> userLoggins;
	private ArrayList<Long> auctionsWithoutBid;
	private double bidPriceMax;
	private long userSessionMin;
	private long userSessionMax;
	private long allUserCount;
	private long allBidCount;
	private long allAuctionCount;
	private long allUserTime;
	private long allAuctionTime;
	private long auctionSuccessCount;
	private long analyticsServerStarted;

	public AnalyticsServerImpl() {
		super();
		this.subscriptions = new ArrayList<Subscription>();
		this.userLoggins = new HashMap<String, Long>();
		this.analyticsServerStarted = System.currentTimeMillis();
		this.auctionsWithoutBid = new ArrayList<Long>();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see server.analytics.AnalyticsServer#subscribe(java.lang.String)
	 */
	@Override
	public synchronized Subscription subscribe(String regex,
			ManagementClientNotifier clientNotifier) throws RemoteException {

		SubscriptionImpl s = new SubscriptionImpl(clientNotifier);
		subscriptions.add(s);

		return s;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see server.analytics.AnalyticsServer#unsubscribe(java.lang.String)
	 */
	@Override
	public synchronized String unsubscribe(Subscription subscription)
			throws RemoteException {

		return "nothing to do";
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see server.analytics.AnalyticsServer#processEvent(model.event.Event)
	 */
	@Override
	public synchronized void processEvent(Event event) throws RemoteException {
		// Notificate subscribers of this event
		sendToSubscribers(event);

		if (event.getType().equals(EventType.BID_PLACED)) {
			allBidCount++;
			BidEvent bidEvent = (BidEvent) event;
			
			if (bidEvent.getPrice() > bidPriceMax) {
				this.bidPriceMax = bidEvent.getPrice();
				sendToSubscribers(new StatisticsEvent(EventType.BID_PRICE_MAX,
						bidPriceMax));
			}
			
			for (long id : auctionsWithoutBid) {
				if (id == bidEvent.getAuctionId()) {
					auctionSuccessCount++;
					auctionsWithoutBid.remove(id);
					break;
				}
			}

			double minutesAlive = (double) ((double) (System
					.currentTimeMillis() - analyticsServerStarted) / 1000 / 60);

			sendToSubscribers(new StatisticsEvent(
					EventType.BID_COUNT_PER_MINUTE,
					(double) ((int) (((double) (allBidCount / minutesAlive)) * 1000)) / 1000));
		}

		else if (event.getType().equals(EventType.AUCTION_STARTED)) {
			allAuctionCount++;
			AuctionEvent auctionEvent = (AuctionEvent) event;
			auctionsWithoutBid.add(auctionEvent.getAuctionId());
			
			allAuctionTime += auctionEvent.getDuration();
			sendToSubscribers(new StatisticsEvent(
					EventType.AUCTION_TIME_AVG,
					(double) ((int) (((double) allAuctionTime / allAuctionCount) * 1000)) / 1000));
		}

		else if (event.getType().equals(EventType.AUCTION_ENDED)) {
			
			sendToSubscribers(new StatisticsEvent(
					EventType.AUCTION_SUCCESS_RATIO,
					(double) ((int) (((double) auctionSuccessCount / allAuctionCount) * 1000)) / 1000));
			
			auctionsWithoutBid.remove(((AuctionEvent)event).getAuctionId());
		}

		else if (event.getType().equals(EventType.USER_LOGIN)) {
			UserEvent loginEvent = (UserEvent) event;
			userLoggins.put(loginEvent.getUserName(),
					System.currentTimeMillis());
		} 
		
		else if (event.getType().equals(EventType.USER_LOGOUT)
				|| event.getType().equals(EventType.USER_DISCONNECTED)) {
			UserEvent logoutEvent = (UserEvent) event;

			long loggedInDuration = System.currentTimeMillis()
					- userLoggins.get(logoutEvent.getUserName());
			userLoggins.remove(logoutEvent.getUserName());

			allUserTime += loggedInDuration;
			allUserCount++;

			if (loggedInDuration < userSessionMin || userSessionMin == 0) {
				userSessionMin = loggedInDuration;
				sendToSubscribers(new StatisticsEvent(
						EventType.USER_SESSIONTIME_MIN, (userSessionMin / 1000)));
			}
			if (loggedInDuration > userSessionMax) {
				userSessionMax = loggedInDuration;
				sendToSubscribers(new StatisticsEvent(
						EventType.USER_SESSIONTIME_MAX, (userSessionMax / 1000)));
			}
			sendToSubscribers(new StatisticsEvent(
					EventType.USER_SESSIONTIME_AVG,
					(allUserTime / allUserCount / 1000)));
		}
	}

	/**
	 * Sends the occurred event to all subscribers, where the event-type is
	 * checked for all regular expression filters. If a remoteexception occurs
	 * it can be assumed that the clientmanagement shut down and so that
	 * subscriber is being deleted from the subscriptions-list.
	 * 
	 * @param event
	 *            the event that occurred.
	 * @throws RemoteException
	 *             thrown if any remote-exception occurs.
	 */
	public void sendToSubscribers(Event event) throws RemoteException {
		ArrayList<Subscription> fails = new ArrayList<Subscription>();
		for (Subscription s : subscriptions) {
			try {
				s.notificate(event);
			} catch (RemoteException e) {
				fails.add(s);
			}
		}
		for (Subscription s : fails) {
			UnicastRemoteObject.unexportObject(s, true);
			subscriptions.remove(s);
		}
	}

	/**
	 * Unexports all remote-subscriptions.
	 */
	public void closeAnalyticsServer() {
		for (Subscription s : subscriptions) {
			try {
				UnicastRemoteObject.unexportObject(s, true);
			} catch (NoSuchObjectException e) {
				System.err
						.println("ERROR: unexporting subscriptions on Analyticsserver: "
								+ e.getMessage());
			}
		}
	}
}
